use std::{cell::RefCell, rc::Rc};

use jnim::*;

use super::android;
use super::Application;

mod proxy;
pub use proxy::*;

#[derive(Clone, Copy)]
pub struct AppRef;
impl types::AppRef for AppRef {
	type Proxy = AppProxy;
	fn exit(&self) {
		// not surport
		// or send terminate message
	}
	fn proxy(&self) -> AppProxy {
		AppProxy(CACHE.with_borrow(|vars| vars.sender.clone()))
	}
}

pub struct AppHandle;
impl AppHandle {
	pub fn singleton() -> Self {
		Self
	}
	pub fn assets() -> Option<ndkm::Assets> {
		with_app(|app| app.assets.clone())
	}
	pub fn assets_java(env: &JEnv) -> Option<JRc<android::content::res::AssetManager>> {
		with_app(|app| {
			if let Some(aj) = &app.assets_java {
				return aj.clone(env);
			}
			None
		})
	}
}

pub fn application_launched() {
	with_app(|app| {
		app.gate.as_ref()?.on_launch(AppRef);
		Some(())
	});
}
pub fn application_terminate() {
	with_app(|app| {
		app.gate.as_ref()?.on_terminate(AppRef);
		Some(())
	});
	with_app_mut(|vars| {
		vars.recver = None;
		vars.gate = None;
		vars.assets = None;
		vars.assets_java = None;
		Some(())
	});
}

pub(crate) fn bind_global_if_need(env: &JEnv, context: &android::content::Context) {
	with_app_mut(|app| {
		if app.context.is_none() {
			app.context = context.global(env);
			let assets_java = context.get_assets(env)?;
			app.assets_java = assets_java.global(env);
			let assets = Some(ndkm::Assets::from_java(env.as_sys(), assets_java.as_sys()));
			app.assets = assets;
		}
		Some(())
	});
}

pub struct App {
	gate: Option<Rc<dyn types::AppRep<Application>>>,
	assets: Option<ndkm::Assets>,
	assets_java: Option<JRc<android::content::res::AssetManager>>,
	context: Option<JRc<android::content::Context>>,
	sender: ndkm::Sender<i64>,
	recver: Option<(Rc<AppCmd>, ndkm::LooperGuard<AppCmd>)>,
}

thread_local! {
	static CACHE:RefCell<App> = RefCell::new({
		let (sender, recver) = ndkm::pipe();
		let fd = recver.fd_read;
		let cmd = Rc::new(AppCmd(recver));
		let recver = ndkm::Looper::current().map(|v|v.add(fd, &cmd).ok()).flatten().map(|r|(cmd,r));
		App {
			gate:None,
			assets: None,
			assets_java:None,
			context:None,
			sender,
			recver:recver,
		}
	});
}
pub(crate) fn app_init(rep: impl types::AppRep<Application>) {
	CACHE.with_borrow_mut(|vars| vars.gate = Some(Rc::new(rep)))
}
fn with_app_mut<T>(mut fun: impl FnMut(&mut App) -> Option<T>) -> Option<T> {
	CACHE.with_borrow_mut(|app| fun(app))
}
fn with_app<T>(mut fun: impl FnMut(&App) -> Option<T>) -> Option<T> {
	CACHE.with_borrow(|app| fun(app))
}
